/*
 * JBoss, Home of Professional Open Source
 * Copyright 2011, Red Hat, Inc. and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

/**
 * Start time:12:14:57 2009-09-04<br>
 * Project: mobicents-isup-stack<br>
 * 
 * @author <a href="mailto:baranowb@gmail.com">Bartosz Baranowski </a>
 */
package org.mobicents.protocols.ss7.isup.impl;

import java.io.IOException;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.mobicents.protocols.ss7.isup.CircuitManager;
import org.mobicents.protocols.ss7.isup.ISUPMessageFactory;
import org.mobicents.protocols.ss7.isup.ISUPParameterFactory;
import org.mobicents.protocols.ss7.isup.ISUPProvider;
import org.mobicents.protocols.ss7.isup.ISUPStack;
import org.mobicents.protocols.ss7.isup.ParameterException;
import org.mobicents.protocols.ss7.isup.impl.message.AbstractISUPMessage;
import org.mobicents.protocols.ss7.mtp.Mtp3;
import org.mobicents.protocols.ss7.mtp.Mtp3PausePrimitive;
import org.mobicents.protocols.ss7.mtp.Mtp3ResumePrimitive;
import org.mobicents.protocols.ss7.mtp.Mtp3StatusPrimitive;
import org.mobicents.protocols.ss7.mtp.Mtp3TransferPrimitive;
import org.mobicents.protocols.ss7.mtp.Mtp3UserPart;
import org.mobicents.protocols.ss7.mtp.Mtp3UserPartListener;

import org.mobicents.protocols.ss7.scheduler.Scheduler;

/**
 * Start time:12:14:57 2009-09-04<br>
 * Project: mobicents-isup-stack<br>
 * 
 * @author <a href="mailto:baranowb@gmail.com">Bartosz Baranowski </a>
 */
public class ISUPStackImpl implements ISUPStack, Mtp3UserPartListener {

	private Logger logger = Logger.getLogger(ISUPStackImpl.class);

	private State state = State.IDLE;
	// dont quite like the idea of so many threads... but.
	// private ExecutorService executor;
	// private ExecutorService layer3exec;

	// protected ConcurrentLinkedQueue<byte[]> txDataQueue = new
	// ConcurrentLinkedQueue<byte[]>();

	private Mtp3UserPart mtp3UserPart = null;
	private CircuitManager circuitManager = null;
	private ISUPProviderImpl provider;
	// local vars
	private ISUPMessageFactory messageFactory;
	private ISUPParameterFactory parameterFactory;

	private Scheduler scheduler;
	
	public ISUPStackImpl(Scheduler scheduler,int localSpc,int ni) {
		super();
		this.scheduler=scheduler;		
		this.provider = new ISUPProviderImpl(this, scheduler , ni, localSpc);
		this.parameterFactory = this.provider.getParameterFactory();
		this.messageFactory = this.provider.getMessageFactory();
		
		this.state = State.CONFIGURED;
	}

	public ISUPProvider getIsupProvider() {
		return provider;
	}

	public void start() throws IllegalStateException {
		if (state != State.CONFIGURED) {
			throw new IllegalStateException("Stack has not been configured or is already running!");
		}
		if (state == State.RUNNING) {
			// throw new StartFailedException("Can not start stack again!");
			throw new IllegalStateException("Can not start stack again!");
		}
		if (this.mtp3UserPart == null) {
			throw new IllegalStateException("No Mtp3UserPart present!");
		}

		if (this.circuitManager == null) {
			throw new IllegalStateException("No CircuitManager present!");
		}
		// this.executor = Executors.newFixedThreadPool(1);
		// this.layer3exec = Executors.newFixedThreadPool(1);
		this.provider.start();
		// this.layer3exec.execute(new MtpStreamHandler());

		this.mtp3UserPart.addMtp3UserPartListener(this);

		this.state = State.RUNNING;

	}

	public void stop() {
		if (state != State.RUNNING) {
			throw new IllegalStateException("Stack is not running!");
		}
		// if(state == State.CONFIGURED)
		// {
		// throw new IllegalStateException("Can not stop stack again!");
		// }

		this.mtp3UserPart.removeMtp3UserPartListener(this);

		// this.executor.shutdown();
		// this.layer3exec.shutdown();
		this.provider.stop();
		this.state = State.CONFIGURED;

	}

	// ///////////////
	// CONF METHOD //
	// ///////////////
	/**
     *
     */
	/*public void configure(Properties props) {
		if (state != State.IDLE) {
			throw new IllegalStateException("Stack already been configured or is already running!");
		}

		
		this.state = State.CONFIGURED;
	}*/

	public Mtp3UserPart getMtp3UserPart() {
		return mtp3UserPart;
	}

	public void setMtp3UserPart(Mtp3UserPart mtp3UserPart) {
		this.mtp3UserPart = mtp3UserPart;
	}

	public void setCircuitManager(CircuitManager mgr) {
		this.circuitManager = mgr;

	}

	public CircuitManager getCircuitManager() {
		return this.circuitManager;
	}

	// ---------------- private methods and class defs

	/**
	 * @param message
	 */
	void send(Mtp3TransferPrimitive message) throws IOException {
		
		if (this.state != State.RUNNING)
			return;
		
		// here we have encoded msg, nothing more, need to add MTP3 label.
		// txDataQueue.add(message);
		try {
			this.mtp3UserPart.sendMessage(message);
		} catch (IOException e) {
			// log here Exceptions from MTP3 level
			logger.error("IOException when sending the message to MTP3 level: " + e.getMessage(), e);
			e.printStackTrace();
			throw e;
		}
	}

	private enum State {
		IDLE, CONFIGURED, RUNNING;
	}

	@Override
	public void onMtp3PauseMessage(Mtp3PausePrimitive arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onMtp3ResumeMessage(Mtp3ResumePrimitive arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onMtp3StatusMessage(Mtp3StatusPrimitive arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onMtp3TransferMessage(Mtp3TransferPrimitive mtpMsg) {
		// int commandCode = msu[7];
		// http://pt.com/page/tutorials/ss7-tutorial/mtp
		// byte[] payload = new byte[msu.length - 5];
		// System.arraycopy(msu, 5, payload, 0, payload.length);
		// byte sls = msu[5];
		// for post processing
		// AbstractISUPMessage msg = (AbstractISUPMessage)
		// messageFactory.createCommand(commandCode);
		// msg.decode(payload, parameterFactory);
		// msg.setSls(sls); // store SLS...
		// return msg;
		
		if (this.state != State.RUNNING)
			return;

		// process only ISUP messages
		if (mtpMsg.getSi() != Mtp3._SI_SERVICE_ISUP)
			return;
		
		// 1(SIO) + 3(RL) + 1(SLS) + 2(CIC) + 1(CODE)
		byte[] payload = mtpMsg.getData();
		int commandCode = payload[2];

		AbstractISUPMessage msg = (AbstractISUPMessage) messageFactory.createCommand(commandCode);
		try {
			msg.decode(payload, parameterFactory);
		} catch (ParameterException e) {
			logger.error("Error decoding of incoming Mtp3TransferPrimitive" + e.getMessage(), e);
			e.printStackTrace();
		}
		msg.setSls(mtpMsg.getSls()); // store SLS...
		//should take here OPC or DPC????? since come in different direction looks like opc
		provider.receive(msg, mtpMsg.getOpc());
	}

	// private class MtpStreamHandler implements Runnable {
	// ByteBuffer rxBuffer = ByteBuffer.allocateDirect(1000);
	// ByteBuffer txBuffer = ByteBuffer.allocateDirect(1000);
	// int rxBytes = 0;
	// @SuppressWarnings("unused")
	// int txBytes = 0;
	//
	// public void run() {
	// // Execute only till state is Running
	// while (state == State.RUNNING) {
	//
	// try {
	// //Execute the MTP3UserPart
	// mtp3UserPart.execute();
	//
	// rxBytes = 0;
	// rxBuffer.clear();
	// try {
	// rxBytes = mtp3UserPart.read(rxBuffer);
	// if (rxBytes != 0) {
	// byte[] data = new byte[rxBytes];
	// rxBuffer.flip();
	// rxBuffer.get(data);
	// MessageHandler handler = new MessageHandler(data);
	// executor.execute(handler);
	// }
	// } catch (IOException e) {
	// logger.error("Error while readig data from Mtp3UserPart", e);
	// }
	//
	// // Iterate till we send all data
	// while (!txDataQueue.isEmpty()) {
	// txBuffer.clear();
	// txBuffer.put(txDataQueue.poll());
	// txBuffer.flip();
	// try {
	// txBytes = mtp3UserPart.write(txBuffer);
	// } catch (IOException e) {
	// logger.error("Error while writting data to Mtp3UserPart", e);
	// }
	// }// while txDataQueue
	// } catch (IOException e1) {
	// // TODO Auto-generated catch block
	// e1.printStackTrace();
	// }
	// }// end of while
	// }
	// }
	//
	// private class MessageHandler implements Runnable {
	// // MSU as input stream
	// private byte[] msu;
	// private ISUPMessage message;
	//
	// protected MessageHandler(byte[] msu) {
	// this.msu = msu;
	//
	// }
	//
	// private ISUPMessage parse() throws IOException {
	// try {
	// // FIXME: change this, dont copy over and over?
	//
	// int commandCode = msu[7];// 1(SIO) + 3(RL) + 1(SLS) + 2(CIC) + 1(CODE)
	// // http://pt.com/page/tutorials/ss7-tutorial/mtp
	// byte[] payload = new byte[msu.length - 5];
	// System.arraycopy(msu, 5, payload, 0, payload.length);
	// byte sls = msu[5];
	// // for post processing
	// AbstractISUPMessage msg = (AbstractISUPMessage)
	// messageFactory.createCommand(commandCode);
	// msg.decode(payload, parameterFactory);
	// msg.setSls(sls); //store SLS...
	// return msg;
	//
	// } catch (Exception e) {
	// // FIXME: what should we do here? send back?
	// e.printStackTrace();
	// logger.error("Failed on data: " + Utils.hexDump(null, msu));
	// }
	// return null;
	// }
	//
	// public void run() {
	// if (message == null) {
	// try {
	// message = parse();
	// } catch (IOException e) {
	// logger.warn("Corrupted message received");
	// return;
	// }
	// }
	// // deliver to provider, so it can check up on circuit, play with
	// // timers and deliver.
	// if(message!=null)
	// {
	// try{
	// provider.receive(message);
	// }catch(Exception e)
	// {
	// //TODO: add proper answer?
	// }
	// }
	// }
	// }

}
